/*!
\copyright  Copyright (c) 2023 Qualcomm Technologies International, Ltd.
            All Rights Reserved.
            Qualcomm Technologies International, Ltd. Confidential and Proprietary.
\file
\brief      Sync start A2dp - utility functions

*/

#include "sync_start_a2dp_utils.h"
#include "sync_start_a2dp.h"
#include "sync_start_a2dp_audio.h"
#include "sync_start_a2dp_config.h"
#include "sync_start_a2dp_types.h"
#include "sync_start_a2dp_ctx.h"

#ifdef INCLUDE_AUDIO_STACK
#include "dsp_clock.h"
#include "power_manager.h"
#endif

#include <q2q_mode.h>
#include <output_mute.h>

#include <mirror_profile_protected.h>
#include <kymera_internal_msg_ids.h>

#include <timestamp_event.h>
#include <logging.h>

#include <message.h>
#include <panic.h>

void SyncStartA2dp_SubscribeForMessageMoreData(Source source, Task task)
{
    MessageStreamTaskFromSource(source, task);
    PanicFalse(SourceConfigure(source, VM_SOURCE_MESSAGES, VM_MESSAGES_SOME));

    MessageSendLater(task,
                       KYMERA_INTERNAL_A2DP_MESSAGE_MORE_DATA_TIMEOUT,
                       NULL, A2DP_MIRROR_MESSAGE_MORE_DATA_TIMEOUT_MS);
}

void SyncStartA2dp_UnsubscribeFromMessageMoreData(Source source, Task task)
{
    MessageCancelAll(task, KYMERA_INTERNAL_A2DP_MESSAGE_MORE_DATA_TIMEOUT);

    /* Not interested in any more messages */
    SourceConfigure(source, VM_SOURCE_MESSAGES, VM_MESSAGES_NONE);
    MessageStreamTaskFromSource(source, NULL);
    MessageCancelAll(task, MESSAGE_MORE_DATA);
}

void SyncStartA2dp_ScheduleDataSyncIndTimeoutMessage(sync_start_a2dp_info_t *sync_info)
{
    if (sync_info->mode != KYMERA_AUDIO_SYNC_START_PRIMARY_UNSYNCHRONISED)
    {
        /* No timeout is required if the MESSAGE_MORE_DATA is received
           after the data sync indication. The state is set to
           KYMERA_AUDIO_SYNC_STATE_IN_PROGRESS when data sync indication
           is received */
        if (sync_info->state == KYMERA_AUDIO_SYNC_STATE_INIT)
        {
            /* Cancel any pending timeout messages */
            MessageCancelAll(SyncStartA2dp_GetTask(), KYMERA_INTERNAL_A2DP_DATA_SYNC_IND_TIMEOUT);

            /* schedule a message to start audio in unsychronised mode if A2DP data sync
            * indication doesn't arrive within expected time.
            */
            MessageSendLater(SyncStartA2dp_GetTask(),
                            KYMERA_INTERNAL_A2DP_DATA_SYNC_IND_TIMEOUT,
                            NULL, A2DP_MIRROR_DATA_SYNC_IND_TIMEOUT_MS);
        }
    }

}

/* Schedule and send a message which will trigger audio unmute when both
   earbuds' audio is synchronised. */
static void syncStartA2dp_SendAudioSynchronisedMessage(rtime_t sync_time)
{
    rtime_t sched_delay = RtimeTimeToMsDelay(sync_time);

    /* Cancel any pending/timeout messages and schedule a message to unmute
        the audio output just after audio sync stream will be in sync. */
    MessageCancelAll(SyncStartA2dp_GetTask(), KYMERA_INTERNAL_A2DP_AUDIO_SYNCHRONISED);
    MessageSendLater(SyncStartA2dp_GetTask(),
                    KYMERA_INTERNAL_A2DP_AUDIO_SYNCHRONISED,
                    NULL,
                    sched_delay);

    DEBUG_LOG("SyncStartA2dp_SendAudioSynchronisedMessage, sync in %dms", sched_delay);
}

void SyncStartA2dp_SetUnmuteTime(rtime_t unmute_time)
{
    sync_start_a2dp_stat_t state = SyncStartA2dp_GetInfo()->state;

    DEBUG_LOG("appKymeraA2dpSetSyncUnmuteTime, theKymera->sync_info.state %d, unmute time %x", state, unmute_time);
    if (SyncStartA2dp_IsA2dpMirrored() && (state != KYMERA_AUDIO_SYNC_STATE_COMPLETE))
    {
        syncStartA2dp_SendAudioSynchronisedMessage(unmute_time);
    }
}

void SyncStartA2dp_ScheduleUnmute(rtime_t time_instant_us, rtime_t delay_us)
{
    rtime_t sync_time = time_instant_us + delay_us;

#ifdef INCLUDE_MIRRORING
    MirrorProfile_SendA2pdUnmuteTimeToPrimary(sync_time);
#endif
    syncStartA2dp_SendAudioSynchronisedMessage(sync_time);
}

void SyncStartA2dp_ScheduleUnmuteTimeout(void)
{
    MessageSendLater(SyncStartA2dp_GetTask(), KYMERA_INTERNAL_A2DP_AUDIO_SYNCHRONISED, NULL,
                            A2DP_MIRROR_SYNC_UNMUTE_TIMEOUT_MS);
}

void SyncStartA2dp_ScheduleQ2qUnmute(sync_start_a2dp_mode_t mode, rtime_t delay_us)
{
    if (mode == KYMERA_AUDIO_SYNC_START_SECONDARY_SYNC_UNMUTE)
    {
        /* The secondary normally calls MirrorProfile_SendA2pdUnmuteTimeToPrimary() when it handles the
        audio sync stream indication (MESSAGE_SOURCE_AUDIO_SYNCHRONISED) but with Q2Q, there is no audio
        sync stream ind, and so there will be no MESSAGE_SOURCE_AUDIO_SYNCHRONISED to trigger the secondary
        to send the sync message. Another way of detecting on the secondary that audio has started streaming
        from the handset is MESSAGE_MORE_DATA. Therefore in Q2Q mode, secondary calls
        MirrorProfile_SendA2pdUnmuteTimeToPrimary() so it can schedule a unmute instant and send the unmute
        time to the primary. Then the primary can unmute at the same time. */
        SyncStartA2dp_ScheduleUnmute(VmGetTimerTime(), delay_us);
    }
    else if (mode == KYMERA_AUDIO_SYNC_START_PRIMARY_SYNC_UNMUTE)
    {
        /* If the secondary doesn't send a audio synchronised message, the primary will never unmute - it
           will remain silent. Therefore a timeout message needs to be sent on the primary. */
        SyncStartA2dp_ScheduleUnmuteTimeout();
    }
}

void SyncStartA2dp_SyncCompleted(void)
{
    DEBUG_LOG("SyncStartA2dp_SyncCompleted");
    SyncStartA2dp_GetInfo()->state = KYMERA_AUDIO_SYNC_STATE_COMPLETE;
#ifdef INCLUDE_AUDIO_STACK
    DspClock_Auto();
    appPowerPerformanceProfileRelinquish();
#endif
}
